// Devein Game Engine
// Written by Danny Rodriguez
// Haxzs Development - 2008
// devein.window/main.cpp
// Revision: $WC

#include "main.h"

int defaultinit()
{
	GLfloat LightAmbient[]=		{ 0.4f, 0.4f, 0.4f, 1.0f };
	GLfloat LightDiffuse[]=		{ 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat LightPosition[]=	{ -2.0f, 0.0f, -3.0f, 1.0f };

    glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping
	glEnable(GL_COLOR_MATERIAL);
	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// Black Background
	glClearDepth(1.0f);									// Depth Buffer Setup
	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

	glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);		// Setup The Ambient Light
	glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);		// Setup The Diffuse Light
	glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);	// Position The Light
	glEnable(GL_LIGHT1);								// Enable Light One
	glEnable(GL_LIGHTING);

	return true;
}

void defaultdraw()
{
	return;
}

int devein::gfx::textureload(char* t)
{
	GLuint textureID = 0;
	// Search the textures for a free ID
	for (int i=0; i<1024; i++)
	{
		if (textures[i] == 0)
		{
			textureID = i;
			break;
		}
	}

	FREE_IMAGE_FORMAT formato = FreeImage_GetFileType(t,0); // Load the image
	if (!formato)
	{
		return -1;
	}
	FIBITMAP* imagen = FreeImage_Load(formato, t);
	if (!imagen)
	{
		return -1;
	}

	FIBITMAP* temp = imagen;
	imagen = FreeImage_ConvertTo32Bits(imagen);
	if (!imagen)
	{
		return -1;
	}
	FreeImage_Unload(temp);

	int w = FreeImage_GetWidth(imagen);
	int h = FreeImage_GetHeight(imagen);

	GLubyte* textura = new GLubyte[4*w*h];
	char* pixeles = (char*)FreeImage_GetBits(imagen);
	//FreeImage loads in BGR format, so you need to swap some bytes(Or use GL_BGR).

	for(int j= 0; j<w*h; j++)
	{
		textura[j*4+0]= pixeles[j*4+2];
		textura[j*4+1]= pixeles[j*4+1];
		textura[j*4+2]= pixeles[j*4+0];
		textura[j*4+3]= pixeles[j*4+3];
	}

	//Now generate the OpenGL texture object 
	glGenTextures(1, &textures[textureID]);
	glBindTexture(GL_TEXTURE_2D, textures[textureID]);
	glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA, w, h, 0, GL_RGBA,GL_UNSIGNED_BYTE,(GLvoid*)textura );
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

	GLenum huboError = glGetError();
	if(huboError)
	{
		return -1;
	}

	return textureID;
}

void devein::gfx::applytexture(int id)
{
	glBindTexture(GL_TEXTURE_2D, textures[id]);
}

int devein::gfx::loadtexture(int id, char* texturefile)
{
	// Assign variables
	GLuint textureid;
	devein::texture texture;

	// Load the texture
	textureid = textureload(texturefile);

	return textureid;
}



devein::gfx::gfx()
{
	done = false;
}

void devein::gfx::calcfps()					// This function calculates FPS
{
	static float fps2           = 0.0f;						
	static float previousTime  = 0.0f;	
	static char  strFPS[20]    = {0};

	float currentTime = (GetTickCount() * 0.001f);				

	++fps2;	// Increment the FPS counter

	if( currentTime - previousTime > 1.0f )
	{
		previousTime = currentTime;
		fps = fps2;
		fps2 = 0.0f;
		sprintf((char *)fpsstring.c_str(), "%i", fps);
		fpsstring.append(" FPS");
	}
}

GLvoid devein::gfx::resize(GLsizei width, GLsizei height)
{
	if (height==0)										
	{
		height=1;										
	}

	glViewport(0,0,width,height);						

	glMatrixMode(GL_PROJECTION);						
	glLoadIdentity();									

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							
	glLoadIdentity();
}

void devein::gfx::draw2dwindows()
{

}

GLvoid devein::gfx::drawscene()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	draw();
	draw2dwindows();
	calcfps();
}

void devein::gfx::initialize()
{
	FreeImage_Initialise();
	if (!init)
	{
		init = defaultinit;
	}
	else
	{
		init();
	}
	if (!draw)
	{
		draw = defaultdraw;
	}
	else
	{
		draw();
	}
}


bool devein::gfx::create(char* title, int width, int height, int bits)
{
	GLuint			PixelFormat;
	WNDCLASS		wc;
	DWORD			dwExStyle;
	DWORD			dwStyle;
	RECT			WindowRect;
	WindowRect.left=(long)0;
	WindowRect.right=(long)width;
	WindowRect.top=(long)0;
	WindowRect.bottom=(long)height;

	hInstance			= GetModuleHandle(NULL);
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc		= (WNDPROC) swndproc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= hInstance;
	wc.hIcon			= LoadIcon(NULL, IDI_WINLOGO);
	wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground	= NULL;
	wc.lpszMenuName		= NULL;
	wc.lpszClassName	= "Devein";

	if (!RegisterClass(&wc))
	{
		MessageBox(NULL,"Failed to Register the Window Class.","Fatal Error",MB_OK | MB_ICONERROR);
		return FALSE;
	}

	if (fullscreen)
	{
		DEVMODE	dmScreenSettings;
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth	= width;
		dmScreenSettings.dmPelsHeight	= height;
		dmScreenSettings.dmBitsPerPel	= bits;
		dmScreenSettings.dmFields		= DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			MessageBox(NULL,"The requested fullscreen mode is not supported by your video card. Reverting to window mode.","Error",MB_OK | MB_ICONINFORMATION);
			fullscreen=FALSE;
		}
	}

	if (fullscreen)
	{
		dwExStyle=WS_EX_APPWINDOW;
		dwStyle=WS_POPUP;
		ShowCursor(FALSE);
	}
	else
	{
		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		dwStyle=WS_OVERLAPPEDWINDOW;
	}

	AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);

	if (!(hWnd=CreateWindowEx( dwExStyle,
		"Devein",
		title,
		dwStyle |
		WS_CLIPSIBLINGS |
		WS_CLIPCHILDREN,
		0, 0,
		WindowRect.right-WindowRect.left,
		WindowRect.bottom-WindowRect.top,
		NULL,
		NULL,
		hInstance,
		this)))
	{
		kill();
		MessageBox(NULL,"Window creation error.","Fatal Error",MB_OK|MB_ICONERROR);
		return FALSE;
	}

	static	PIXELFORMATDESCRIPTOR pfd=
	{
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		bits,
		0, 0, 0, 0, 0, 0,
		0,
		0,
		0,
		0, 0, 0, 0,
		16,
		0,
		0,
		PFD_MAIN_PLANE,
		0,
		0, 0, 0
	};

	if (!(hDC=GetDC(hWnd)))
	{
		kill();
		MessageBox(NULL,"Can't create a GL Device Context.","Fatal Error",MB_OK|MB_ICONERROR);
		return FALSE;
	}

	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))
	{
		kill();
		MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	if(!SetPixelFormat(hDC,PixelFormat,&pfd))
	{
		kill();
		MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	if (!(hRC=wglCreateContext(hDC)))
	{
		kill();
		MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	if(!wglMakeCurrent(hDC,hRC))
	{
		kill();
		MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	ShowWindow(hWnd,SW_SHOW);
	SetForegroundWindow(hWnd);
	SetFocus(hWnd);
	resize(width, height);
	initialize();
	return TRUE;
}

GLvoid devein::gfx::kill()
{
	if (fullscreen)
	{
		ChangeDisplaySettings(NULL,0);
		ShowCursor(TRUE);
	}

	if (hRC)
	{
		if (!wglMakeCurrent(NULL,NULL))
		{
			MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(hRC))
		{
			MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		hRC=NULL;
	}

	if (hDC && !ReleaseDC(hWnd,hDC))
	{
		MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hDC=NULL;
	}

	if (hWnd && !DestroyWindow(hWnd))
	{
		MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hWnd=NULL;
	}

	if (!UnregisterClass("Devein",hInstance))
	{
		MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hInstance=NULL;
	}
}

LRESULT CALLBACK devein::gfx::swndproc(HWND hWnd, UINT	uMsg, WPARAM wParam, LPARAM	lParam)
{
	LPCREATESTRUCT cs;

	if (uMsg == WM_CREATE)
	{
		cs = (LPCREATESTRUCT) lParam;
		SetWindowLong(hWnd, GWL_USERDATA, (LONG) cs->lpCreateParams);
	}

	devein::gfx *gfx = (devein::gfx *) GetWindowLong(hWnd, GWL_USERDATA);
	return gfx->wndproc(hWnd, uMsg, wParam, lParam);

}

LRESULT devein::gfx::wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_ACTIVATE:
		{
			if (!HIWORD(wParam))
			{
				active=TRUE;
			}
			else
			{
				active=FALSE;
			}

			return 0;
			break;
		}
	case WM_KEYDOWN:
		{
			if (wParam == VK_ESCAPE)
			{
				done=true;
			}
			keys[wParam] = TRUE;
			break;
		}
	case WM_KEYUP:
		{
			keys[wParam] = FALSE;
			break;
		}
	case WM_SYSCOMMAND:
		{
			switch(wParam)
			{
			case SC_SCREENSAVE:
			case SC_MONITORPOWER:
				return 0;
			}
			break;
		}
	case WM_CLOSE:
		{
			done = true;
			return 0;
			break;
		}
	case WM_SIZE:
		{
			resize(LOWORD(lParam),HIWORD(lParam));
			return 0;
			break;
		}

	}

	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

int devein::gfx::msgloop()
{
	MSG msg;
	ZeroMemory(&msg, sizeof(MSG));

	while (!done)
	{
		if(msg.message == WM_QUIT)
		{
			done = true;
		}
		else
		{
			if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			else
			{
				drawscene();
				SwapBuffers(hDC);
			}
		}
	}
	return (int)msg.wParam;
}


